#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "base_buffer.h"
#include "base_log.h"

namespace El {
namespace Base {

struct Buffer::Internal {
    uint8_t *buf_;
    uint32_t length_;
    uint32_t size_;
    uint32_t incStep_;
};

Buffer::Buffer() : internal_(nullptr) {}

Buffer::~Buffer()
{
    if (internal_) {
        free(internal_->buf_);
    }
}

uint32_t Buffer::PutBuffer(const uint8_t *buffer, uint32_t length)
{
    if (!internal_) {
        uint8_t *buf = (uint8_t *)malloc(length + sizeof(Internal));
        if (buf == nullptr) {
            LOG_ERROR("Buffer putbuffer Malloc length[{}] failed \n", (uint32_t)(length + sizeof(Internal)));
            return 0;
        }
        internal_ = reinterpret_cast<Internal *>(buf);
        internal_->size_ = length;
        internal_->length_ = 0;
        internal_->incStep_ = 0;
        internal_->buf_ = buf;
    } else if (internal_->size_ - internal_->length_ < length) {
        uint32_t size = 0;
        if (internal_->incStep_) {
            size = (internal_->length_ + length + internal_->incStep_ - 1) / internal_->incStep_;
            size = size * internal_->incStep_;
        } else
            size = internal_->length_ + length;
        uint8_t *buf = (uint8_t *)malloc(size + sizeof(Internal));
        if (buf == nullptr) {
            LOG_ERROR("Buffer putbuffer ReMalloc length[{}] failed \n", (uint32_t)(length + sizeof(Internal)));
            return 0;
        }
        memcpy(buf, internal_->buf_, internal_->length_ + sizeof(Internal));
        free(internal_->buf_);
        internal_ = reinterpret_cast<Internal *>(buf);
        internal_->size_ = size;
        internal_->buf_ = buf;
    }
    memcpy(internal_->buf_ + internal_->length_ + sizeof(Internal), buffer, length);
    internal_->length_ += length;
    return length;
}

uint8_t *Buffer::GetBuffer() const
{
    if (internal_ && internal_->length_ > 0) {
        return internal_->buf_ + sizeof(Internal);
    }
    return nullptr;
}

uint32_t Buffer::GetLength() const
{
    if (internal_) {
        return internal_->length_;
    }
    return 0;
}

void Buffer::Resize(uint32_t size)
{
    if (!internal_) {
        uint8_t *buf = (uint8_t *)malloc(size + sizeof(Internal));
        if (buf == nullptr) {
            LOG_ERROR("Buffer Resize Malloc length[{}] failed\n", (uint32_t)(size + sizeof(Internal)));
            return;
        }
        internal_ = reinterpret_cast<Internal *>(buf);
        internal_->size_ = size;
        internal_->incStep_ = 0;
        internal_->buf_ = buf;
    } else if (internal_->size_ < size && size > 0) {
        if (internal_->incStep_) {
            size = (size + internal_->incStep_ - 1) / internal_->incStep_;
            size = size * internal_->incStep_;
        } else {
            size = size;
        }

        uint8_t *buf = (uint8_t *)malloc(size + sizeof(Internal));
        if (buf == nullptr) {
            LOG_ERROR("Buffer Resize ReMalloc length[{}] failed \n", (uint32_t)(size + sizeof(Internal)));
            return;
        }
        memcpy(buf, internal_->buf_, internal_->length_ + sizeof(Internal));
        free(internal_->buf_);
        internal_ = reinterpret_cast<Internal *>(buf);
        internal_->size_ = size;
        internal_->buf_ = buf;
    }
    internal_->length_ = size;

    return;
}

uint32_t Buffer::Capacity() const
{
    if (internal_) {
        return internal_->size_;
    }
    return 0;
}

void Buffer::Reserve(uint32_t capacity)
{
    if (!internal_) {
        uint8_t *buf = (uint8_t *)malloc(capacity + sizeof(Internal));
        if (buf == nullptr) {
            LOG_ERROR("Buffer Resize Malloc length[{}] failed\n", (uint32_t)(capacity + sizeof(Internal)));
            return;
        }
        internal_ = reinterpret_cast<Internal *>(buf);
        internal_->size_ = capacity;
        internal_->length_ = 0;
        internal_->incStep_ = 0;
        internal_->buf_ = buf;
    } else if (internal_->size_ < capacity && capacity > 0) {
        uint8_t *buf = (uint8_t *)malloc(capacity + sizeof(Internal));
        if (buf == nullptr) {
            LOG_ERROR("Buffer Reserve Malloc length[{}] failed\n", (uint32_t)(capacity + sizeof(Internal)));
            return;
        }

        memcpy(buf, internal_->buf_, internal_->length_ + sizeof(Internal));
        free(internal_->buf_);
        internal_ = reinterpret_cast<Internal *>(buf);
        internal_->buf_ = buf;
        internal_->size_ = capacity;
    }
}

void Buffer::Reset()
{
    if (internal_) {
        free(internal_->buf_);
        internal_ = nullptr;
    }
}

void Buffer::SetIncreasedSize(uint32_t increase)
{
    if (!internal_) {
        uint8_t *buf = (uint8_t *)malloc(increase + sizeof(Internal));
        if (buf == nullptr) {
            LOG_ERROR("Buffer SetIncreasedSize Malloc length[{}] failed\n", (uint32_t)(increase + sizeof(Internal)));
            return;
        }
        internal_ = reinterpret_cast<Internal *>(buf);
        internal_->size_ = increase;
        internal_->length_ = 0;
        internal_->buf_ = buf;
    }
    internal_->incStep_ = increase;
}

} // namespace Base
} // namespace El
